package com.hnust.root.common.util;

import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.hnust.root.dao.entity.domain.forward.WxAccount;
import com.hnust.root.web.config.GlobalConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;


import java.time.LocalDateTime;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @Author zhusir
 * @Date 2019/1/27
 * @Desc
 */
@Component(value = "JWTUtil")
public class JWTUtil {
    private static final Log LOGGER = LogFactory.get();

    private static long expire = GlobalConfig.getExpire();

    /**
     * JWT秘钥
     */
    private static final String WX_JWT_SECRET = GlobalConfig.getSecret();

    @Autowired
    private RedisTemplate redisTemplate;

    // ######################### web项目 #################################
    /**
     * 生成签名,5min后过期
     *
     * @param username 用户名
     * @param secret   用户的密码
     * @return 加密的token
     */
    public static String sign(String username, String secret,int userId) {
        Algorithm algorithm = Algorithm.HMAC256(secret);
        LocalDateTime now = LocalDateTime.now();
        Date expireAt = ComUtil.localDateTime2Date(now.plusSeconds(expire));
        // 附带username信息
        return JWT.create()
                .withClaim("username", username)
                .withClaim("userId", userId)
                .withExpiresAt(expireAt)
                .sign(algorithm);
    }

    /**
     * 校验token是否正确
     *
     * @param token  密钥
     * @param secret 用户的密码
     * @return 是否正确
     */
    public static boolean verify(String token, String username, String secret) {
        Algorithm algorithm = Algorithm.HMAC256(secret);
        JWTVerifier verifier = JWT.require(algorithm)
                .withClaim("username", username)
                .build();
        LocalDateTime now = LocalDateTime.now();
        Long expierAt = getExpireAt(token);

        //token已经过期
        if (expierAt > ComUtil.datetimeToTimestamp(now)) {
            return false;
        }
        try {
            //如果抛出异常直接验证失败
            DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (TokenExpiredException e) {
            return false;
        }
    }

    /**
     * 获得token中的信息无需secret解密也能获得
     *
     * @return token中包含的用户名
     */
    public static String getUsername(String token) {
        DecodedJWT jwt = JWT.decode(token);
        return jwt.getClaim("username").asString();
    }

    public static int getUserId(String token) {
        DecodedJWT jwt = JWT.decode(token);
        return jwt.getClaim("userId").asInt();
    }

    private static Long getExpireAt(String token) {
        DecodedJWT jwt = JWT.decode(token);
        return jwt.getClaim("exp").asLong();
    }


    // ######################### web项目 #################################


    // ######################### 小程序项目 #################################
    /**
     * 专为小程序用户生成的Token
     * @param account
     * @return
     */
    public String createTokenByWxAccount(WxAccount account) {
        String jwtId = UUID.randomUUID().toString();
        Algorithm algorithm = Algorithm.HMAC256(WX_JWT_SECRET);
        Date expireAt = ComUtil.localDateTime2Date(LocalDateTime.now().plusSeconds(expire));
        String token = JWT.create()
                .withClaim("wxOpenId", account.getOpenId())
                .withClaim("sessionKey", account.getSessionKey())
                .withClaim("jwt-id",jwtId)
                .withExpiresAt(expireAt)
                .sign(algorithm);
        //将JWT缓存至redis
        redisTemplate.opsForValue().set("jwt-session-" + jwtId, token, expire, TimeUnit.SECONDS);
        return token;
    }

    //验证token是否有效
    public boolean wxTokenVerify(String token) {
        if (StrUtil.isEmpty(token)) {
            return false;
        }
        try {
            Algorithm algorithm = Algorithm.HMAC256(WX_JWT_SECRET);
            JWTVerifier verifier = JWT.require(algorithm)
                    .withClaim("wxOpenId", getOpenIdByWxToken(token))
                    .withClaim("sessionKey", getSessionKeyByWxToken(token))
                    .acceptExpiresAt(System.currentTimeMillis() + expire)
                    .build();
            verifier.verify(token);
            //延长token的时间
            redisTemplate.opsForValue().set("jwt-session" + getJwtIdByWxToken(token), token, expire, TimeUnit.SECONDS);
            LOGGER.info("token验证通过");
            return true;
        } catch (Exception e) {
            return false;
        }
    }


    public String getOpenIdByWxToken(String token) {
        return JWT.decode(token).getClaim("wxOpenId").asString();
    }

    public String getSessionKeyByWxToken(String token) {
        return JWT.decode(token).getClaim("sessionKey").asString();
    }

    public String getJwtIdByWxToken(String token) {
        return JWT.decode(token).getClaim("jwt-id").asString();
    }

    // ######################### 小程序项目 #################################
}
